import mx.events.EventDispatcher;

import org.molamil.acto.commands.CommandBuilder;
import org.molamil.acto.commands.CommandContext;
import org.molamil.acto.commands.CommandMaster;
import org.molamil.acto.commands.CommandRequest;
import org.molamil.acto.commands.cmd.CmdInit;
import org.molamil.acto.commands.controller.CommandNotFoundError;
import org.molamil.acto.commands.controller.CommandsController;
import org.molamil.acto.commands.factory.BasicCommandFactory;
import org.molamil.acto.core.EventConstants;
import org.molamil.acto.core.Logger;
import org.molamil.commons.EventUtil;
import org.molamil.commons.StringUtil;
import org.molamil.acto.validators.VldInit;
import org.molamil.acto.core.CategorizedListeners;

import mx.utils.Delegate;

import org.molamil.acto.core.CategorizedListener;
import org.molamil.acto.core.AppRole;
import org.molamil.acto.roles.Role;

/**
 * @author Jorge Hernandez
 */
class org.molamil.acto.commands.controller.AbstractCommandsController implements CommandsController {

	
	// Constants
	
	public static var ERROR_COMMAND_NOT_FOUND:String = "ERROR: Command not found: \"{command}\".";
	
	
	// Properties
	
	private static var log:Logger = Logger.getLogger("AbstractCommandsControler");
	
	private var factory:BasicCommandFactory;	private var currentCommands:Array;
	private var cmdListeners:CategorizedListeners;
	private var cmdListenerDelegate:Function;
	
	
	// Constructor
	
	public function AbstractCommandsController() {
		
		log.debug("Constructor");
		
		EventDispatcher.initialize(this);
		
		CmdInit.init();		VldInit.init();
		
		currentCommands = new Array();
		cmdListeners = new CategorizedListeners();
		
		cmdListenerDelegate = Delegate.create(this, callCmdListeners);
		addEventListener(EventConstants.COMMAND_REQUEST, cmdListenerDelegate);		addEventListener(EventConstants.COMMAND_EXECUTE_START, cmdListenerDelegate);		addEventListener(EventConstants.COMMAND_EXECUTE_COMPLETE, cmdListenerDelegate);		addEventListener(EventConstants.COMMAND_EXECUTE_ERROR, cmdListenerDelegate);
		
	}

	
	// Functions
	
	public function dispatchEvent(eventObj:Object):Void {}
	public function addEventListener(event:String, handler):Void {}
	public function removeEventListener(event:String, handler):Void {}
	
	public function execute(name:String, params:Object):CommandMaster {
		
		log.info("execute(), name = " + name);
		
		// Create the master.
		var master:CommandMaster = null;
		
		// Get the context.
		var context:CommandContext = factory.getCommand(name);
			
		// Get the requested context.
		// FIXME: It seems to crash badly if I throw the error.
		if (context == null) {
			return;
			//var msg:String = StringUtil.replace(ERROR_COMMAND_NOT_FOUND, "{command}", name);
			//throw new CommandNotFoundError(msg);	
		}
		
		// Throw command request event.
		var eventObject:Object = {target: context, type: EventConstants.COMMAND_REQUEST, name: name};
		dispatchEvent(eventObject);
		
		// Check the role.
		if (context.role != null && context.role != "" && context.role != AppRole.getInstance().currentRole.name) {
			log.warning("invalid role, required: " + context.role);			log.warning("invalid role, found: " + AppRole.getInstance().currentRole.name);
			var eventObject:Object = {target: context, type: EventConstants.ROLE_REQUIRED_ERROR, name: name};
			dispatchEvent(eventObject);
			return;
		}
			
		// Create the request.
		var request:CommandRequest = new CommandRequest();
		request.name = name;
		request.params = params;
		
		// Build the master.
		master = CommandBuilder.create(context, request);
			
		// Add the listeners to the requested command.
		EventUtil.propagate(master, this, EventConstants.COMMAND_EXECUTE_START);		EventUtil.propagate(master, this, EventConstants.COMMAND_EXECUTE_COMPLETE);		EventUtil.propagate(master, this, EventConstants.COMMAND_EXECUTE_ERROR);
		
		// Show depends commands.
		doDepends(context);
		
		// Execute.
		master.execute();
		
		return master;
		
	}

	public function containsCommand(name:String):Boolean {
		return factory.containsCommand(name);
	}
	
	public function getCommand(name:String):CommandContext {
		return factory.getCommand(name);
	}
	
	public function getCommands():Array {
		return factory.getCommands();
	}
	
	public function addCmdListener(name:String, event:String, handler):Void {
		cmdListeners.addListener(name, event, handler);
	}
	
	public function removeCmdListener(name:String, event:String, handler):Void {
		cmdListeners.removeListener(name, event, handler);
	}
	
	private function callCmdListeners(eventObject:Object):Void {
		var a:Array = cmdListeners.getListeners(eventObject.name, eventObject.type);
		for (var i:Number = 0; i < a.length; i++) {
			var cl:CategorizedListener = a[i];
			trace("call: " + i);
			if (typeof(cl.handler) == "function") {
				var f:Function = Function(cl.handler);
				f(eventObject);
			} else if (typeof(cl.handler) == "object") {
				var f:Function = Function(cl.handler[eventObject.type]);
				f(eventObject);
			}
		}
	}
	
	private function doDepends(context:CommandContext):Void {
		//TODO: Implement.
	}
	
	
}